﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web.Management;
using NetDon.OA.Data.CollectionClasses;
using NetDon.OA.Data.EntityClasses;
using NetDon.OA.Data.HelperClasses;
using NetDon.OA.Data.Linq;
using NetDon.OA.Interface;
using NetDon.OA.Interface.Configuration;
using NetDon.Utilities;

namespace NetDon.OA.Business
{
    public class SecurityService : ISecurityService, ISecurityManageService
    {
        private readonly ModuleCollection _modules;

        public SecurityService()
        {
            var session = new LinqMetaData();
            IQueryable<FunctionEntity> query = from functions in session.Function
                                               select functions;
            _modules = GetModules(query.ToList());
            _modules.Sort();

            //var collection = new VitalShining.EWS.Data.CollectionClasses.FunctionCollection();
            //if (collection.GetMulti(null))
            //{
            //    _modules = GetModules(collection.ToList());
            //    _modules.Sort();
            //}
        }

        #region ISecurityManageService Members

        public string GetRoleName(Guid roleId)
        {
            var session = new LinqMetaData();
            IQueryable<RoleEntity> query = from entity in session.Role where entity.RoleId == roleId select entity;
            RoleEntity role = query.FirstOrDefault();
            return role != null ? role.RoleName : null;
        }

        public Guid AddRole(string roleName)
        {
            var entity = new RoleEntity { RoleName = roleName };
            if (entity.Save())
            {
                return entity.RoleId;
            }
            return Guid.Empty;
        }

        public bool ChangeRoleName(Guid roleId, string roleName)
        {
            var entity = new RoleEntity(roleId) { RoleName = roleName };
            return entity.Save();
        }

        public bool RemoveRole(Guid roleId)
        {
            var collection = new RoleCollection();
            return collection.DeleteMulti(RoleFields.RoleId == roleId) > 0;
        }

        public RoleInfo[] GetRoles()
        {
            List<RoleEntity> roles = (from role in new LinqMetaData().Role orderby role.RoleName select role).ToList();
            roles.Sort((x, y) => string.Compare(x.RoleName, y.RoleName));
            return roles.Select(item => new RoleInfo { RoleID = item.RoleId, RoleName = item.RoleName }).ToArray();
        }

        public ModuleCollection GetAllModules()
        {
            return _modules;
        }

        public string[] GetRolePermission(Guid roleId)
        {
            var list = new List<string>();
            var collection = new RoleFunctionCollection();
            collection.GetMulti(RoleFunctionFields.RoleId == roleId);
            foreach (RoleFunctionEntity entity in collection)
            {
                list.Add(entity.FunctionId);
            }
            return list.ToArray();
        }

        public bool SavePermission(Guid roleId, string[] permissions, Transaction tran)
        {
            if (permissions == null)
            {
                return false;
            }

            var collection = new RoleFunctionCollection();
            tran.Add(collection);
            collection.DeleteMulti(RoleFunctionFields.RoleId == roleId);
            foreach (string s in permissions)
            {
                collection.Add(new RoleFunctionEntity { RoleId = roleId, FunctionId = s });
            }

            collection.SaveMulti();
            //tran.Commit();
            return true;
        }

        #endregion

        #region ISecurityService Members

        public bool ValidatePermission(Guid userId, string function)
        {
            //modified by peter

            if (userId == Guid.Empty)
            {
                return false;
            }
            InitializePermission(userId);
            if (string.IsNullOrEmpty(function))
            {
                return true;
            }
            var context = (OApplicationContext)ServiceContainer.GetService<ApplicationContext>();
            string[] functions = context.FunctionPermission;
            if (functions == null || functions.Length == 0)
            {
                return false;
            }
            foreach (string featureItem in functions)
            {
                if (string.Compare(function, featureItem, true) == 0)
                {
                    return true;
                }
            }
            return false;
        }

        public Boolean LoginDirect(string userName)
        {
            var session = new LinqMetaData();
            IQueryable<UserEntity> query = from user in session.User
                                           where
                                               user.UserName == userName
                                           select user;
            UserEntity entity = query.FirstOrDefault();
            if (entity != null)
            {
                var context = ServiceContainer.GetService<ApplicationContext>();
                context.UserId = entity.UserId;
                context.UserName = entity.UserName;
                context.ApplicationId = SystemConfiguration.OApplicationId;
                InitializePermission(entity.UserId);
                //InitializeDataFilterPermission(entity.UserId);
                return true;
            }
            return false;
        }

        public bool ValidateUser(string userName, string password, out string message)
        {
            message = string.Empty;
            var session = new LinqMetaData();
            IQueryable<UserEntity> query = from user in session.User
                                           where
                                               user.UserName == userName &&
                                               user.Password == MiscUtilities.ComputePasswordHash(password)
                                           select user;
            UserEntity entity = query.FirstOrDefault();


            var logService = ServiceContainer.GetService<ILogService>();
            var context = (OApplicationContext)ServiceContainer.GetService<ApplicationContext>();
            if (entity != null && entity.UserStatus == (int)UserStatus.Active)
            {
                context.UserId = entity.UserId;
                if (entity.CompanyId.HasValue)
                    context.CompanyId = entity.CompanyId.Value;
                if (entity.Company != null)
                    context.CompanyName = entity.Company.CompanyName;
                context.UserName = entity.UserName;
                context.DepartId = entity.DepartId;
                if (entity.Department != null)
                    context.DepartName = entity.Department.DepartName;
                context.FullName = entity.FullName;
                context.PositionId = entity.PositionId;
                if (entity.GetMultiUserRole(true).FirstOrDefault() != null)
                {
                    context.RoleId = entity.GetMultiUserRole(true).FirstOrDefault().RoleId;
                }
                context.ApplicationId = SystemConfiguration.OApplicationId;
                InitializePermission(entity.UserId);
                //InitializeDataFilterPermission(entity.UserId);
                logService.Log(new LoggingEvent
                {
                    ApplicationId = context.ApplicationId,
                    Category = EventCategories.Security,
                    Level = LogLevel.SuccessAudit,
                    Source = "安全",
                    UserName = context.UserName,
                    Message =
                        "Logon account: " + userName + Environment.NewLine
                    //+
                    //"Logon application: " + new ApplicationEntity(context.ApplicationId).ApplicationName
                });
                HealthMonitoring.RaiseAuthenticationEvent(null, WebEventCodes.AuditFormsAuthenticationSuccess, userName);
                return true;
            }
            if (entity == null)
            {
                message = "登陆失败: 错误的用户名或密码" + Environment.NewLine +
                        "登陆账号: " + userName;
                logService.Log(new LoggingEvent
                {
                    ApplicationId = context.ApplicationId,
                    Category = EventCategories.Security,
                    Level = LogLevel.FailureAudit,
                    Source = "安全",
                    UserName = userName,
                    Message = message

                });
            }
            else if (entity.UserStatus == (int)UserStatus.NotActive)
            {
                message =
                    "登陆失败: 用户没激活。" + Environment.NewLine +
                    "登陆失败: " + userName;
                logService.Log(new LoggingEvent
                {
                    ApplicationId = context.ApplicationId,
                    Category = EventCategories.Security,
                    Level = LogLevel.FailureAudit,
                    Source = "安全",
                    UserName = userName,
                    Message = message
                });
            }

            HealthMonitoring.RaiseAuthenticationEvent(null, WebEventCodes.AuditFormsAuthenticationFailure, userName);
            return false;
        }

        public bool ValidateApplicationAdmin(string userName, string password)
        {
            var session = new LinqMetaData();
            var query = from applicationUser in session.User
                        where

                            applicationUser.Password == MiscUtilities.ComputePasswordHash(password) && applicationUser.UserName == userName

                        select applicationUser;
            var entity = query.FirstOrDefault();
            if (entity != null)
            {
                var context = (ApplicationContext)ServiceContainer.GetService<ApplicationContext>();
                context.UserId = entity.UserId;

                context.ApplicationId = SystemConfiguration.OApplicationId;

                context.UserName = string.IsNullOrEmpty(entity.UserName) ? entity.Email : entity.UserName;
                // InitializeBusinessScope(application.ApplicationId);
                return true;
            }
            return false;
        }

        public bool ApplicationAdminLoginDirect(string userName)
        {
            var session = new LinqMetaData();
            var query = from applicationUser in session.User
                        where

                            applicationUser.UserName == userName

                        select applicationUser;
            var entity = query.FirstOrDefault();
            if (entity != null)
            {
                var context = (ApplicationContext)ServiceContainer.GetService<ApplicationContext>();
                context.UserId = entity.UserId;

                context.ApplicationId = SystemConfiguration.OApplicationId;

                context.UserName = string.IsNullOrEmpty(entity.UserName) ? entity.Email : entity.UserName;
                InitializeBusinessScope(SystemConfiguration.OApplicationId);
                return true;
            }
            return false;
        }

        public bool ChangePassword(Guid userId, string oldPassword, string newPassword)
        {
            string hash = MiscUtilities.ComputePasswordHash(oldPassword);
            var userEntity = new UserEntity(userId);
            if (userEntity.Password != hash)
            {
                return false;
            }
            userEntity.Password = MiscUtilities.ComputePasswordHash(newPassword);
            return userEntity.Save();
        }

        public ModuleCollection GetPermitModules(Guid userId)
        {
            var modules = new ModuleCollection();
            foreach (Module module in GetAllModules())
            {
                if (ValidatePermission(userId, module.ModuleID))
                {
                    var newModule = (Module)module.Clone();
                    modules.Add(newModule);
                    foreach (Function fun in GetPermitFunctions(userId, module.Functions))
                    {
                        newModule.Functions.Add(fun);
                    }
                }
            }
            modules.Sort();
            return modules;
        }

        #endregion

        #region Private Methods

        private static ModuleCollection GetModules(IEnumerable<FunctionEntity> functionEntities)
        {
            IEnumerable<Module> query = from entity in functionEntities
                                        where string.IsNullOrEmpty(entity.ParentId) && entity.Type == 0
                                        select new Module
                                        {
                                            ModuleID = entity.FunctionId,
                                            Name = entity.FunctionName,
                                            ImageUrl = entity.ImageUrl,
                                            Index = entity.Index,
                                            Visible = entity.Visible,
                                        };
            List<Module> list = query.ToList();
            list.ForEach(item => GetFunctions(item, functionEntities));
            return new ModuleCollection(list);
        }

        private static IEnumerable<Function> GetFunctions(string parentId, IEnumerable<FunctionEntity> functionEntities)
        {
            IEnumerable<Function> query = from entity in functionEntities
                                          where entity.ParentId == parentId && entity.Type == 1 && entity.Visible == true
                                          orderby entity.Index ascending
                                          select new Function
                                          {
                                              FunctionID = entity.FunctionId,
                                              Description = entity.Description,
                                              ImageUrl = entity.ImageUrl,
                                              Index = entity.Index,
                                              Name = entity.FunctionName,
                                              NavigateUrl = entity.NavigateUrl,
                                              Visible = entity.Visible,
                                          };
            List<Function> list = query.ToList();
            list.ForEach(item => GetFunctions(item, functionEntities));
            list.ForEach(item => GetOperations(item, functionEntities));
            return list;
        }

        private static void GetFunctions(Module module, IEnumerable<FunctionEntity> functionEntities)
        {
            foreach (Function function in GetFunctions(module.ModuleID, functionEntities))
            {
                function.Module = module;
                module.Functions.Add(function);
            }
            module.Functions.Sort();
        }

        private static void GetFunctions(Function parentFunction, IEnumerable<FunctionEntity> functionEntities)
        {
            foreach (Function function in GetFunctions(parentFunction.FunctionID, functionEntities))
            {
                function.ParentFunction = parentFunction;
                parentFunction.Functions.Add(function);
            }
            parentFunction.Functions.Sort();
        }

        private static void GetOperations(Function parentFunction, IEnumerable<FunctionEntity> functionEntities)
        {
            IEnumerable<Operation> query = from entity in functionEntities
                                           where entity.ParentId == parentFunction.FunctionID && entity.Type == 2
                                           select new Operation
                                                      {
                                                          OperationID = entity.FunctionId,
                                                          Index = entity.Index,
                                                          Name = entity.FunctionName,
                                                          Function = parentFunction
                                                      };
            foreach (Operation op in query)
            {
                parentFunction.Operations.Add(op);
            }
            parentFunction.Operations.Sort();
        }

        private void InitializePermission(Guid userId)
        {
            var context = (OApplicationContext)ServiceContainer.GetService<ApplicationContext>();
            if (context.FunctionPermission == null && !MiscUtilities.IsNullOrEmpty(userId))
            {
                context.FunctionPermission = GetUserPermission(userId);
            }
        }

        //private void InitializeDataFilterPermission(Guid userId)
        //{
        //    var context = (AdminApplicationContext)ServiceContainer.GetService<ApplicationContext>();
        //    if (context.DataFilterPermissions == null && !MiscUtilities.IsNullOrEmpty(userId))
        //    {
        //        context.DataFilterPermissions = GetDataFilterPermission(userId);
        //    }
        //}

        private string[] GetUserPermission(Guid userId)
        {
            var session = new LinqMetaData();
            var query = from roleFunction in session.RoleFunction
                        join userRole in session.UserRole on roleFunction.RoleId equals
                            userRole.RoleId
                        where userRole.UserId == userId
                        select roleFunction;
            var functions = new List<string>();
            foreach (RoleFunctionEntity entity in query.ToList())
            {
                if (!functions.Contains(entity.FunctionId))
                {
                    functions.Add(entity.FunctionId);
                }
            }
            return functions.ToArray();
        }

        /// <summary>
        /// TODO:check datafilter
        /// </summary>
        /// <param name="userId"></param>
        /// <returns>Dictionary<string, IEnumerable<DataFilterValue>></returns>
        //private Dictionary<string, IEnumerable<DataFilterValue>> GetDataFilterPermission(Guid userId)
        //{
        //    var session = new LinqMetaData();
        //    var query = from dataFilter in session.DataFilter
        //                join userRole in session.UserRole on dataFilter.RoleId equals userRole.RoleId
        //                where userRole.UserId == userId
        //                select dataFilter;
        //    var list = query.ToList();
        //    var config = (DataFilterSection)ConfigurationManager.GetSection(DataFilterNames.ConfigurationSectionName);
        //    var permissions = new Dictionary<string, IEnumerable<DataFilterValue>>();
        //    foreach (NameTypeConfigurationElement dataFilter in config.DataFilters)
        //    {
        //        permissions.Add(dataFilter.Name,
        //                        list.Where(d => d.DataFilterName == dataFilter.Name).Select(d => new DataFilterValue
        //                        {
        //                            Value = d.DataFilterValue,
        //                            Options = d.DataFilterValueOptions
        //                        }).ToArray());
        //    }
        //    return permissions;
        //    return new Dictionary<string, IEnumerable<DataFilterValue>>();
        //}

        private void InitializeBusinessScope(Guid applicationId)
        {
        }

        private IEnumerable<Function> GetPermitFunctions(Guid userId, IEnumerable<Function> functions)
        {
            var list = new List<Function>();
            foreach (Function fun in functions)
            {
                if (ValidatePermission(userId, fun.FunctionID))
                {
                    var newFunction = (Function)fun.Clone();
                    list.Add(newFunction);
                    foreach (Function operation in GetPermitFunctions(userId, fun.Functions))
                    {
                        newFunction.Functions.Add(operation);
                    }
                    foreach (Operation operation in GetPermitOperations(userId, fun.Operations))
                    {
                        newFunction.Operations.Add(operation);
                    }
                }
            }
            list.Sort((x, y) => Comparer<int>.Default.Compare(x.Index, y.Index));
            return list;
        }

        private IEnumerable<Operation> GetPermitOperations(Guid userId, IEnumerable<Operation> operations)
        {
            var list = new List<Operation>();
            foreach (Operation operation in operations)
            {
                if (ValidatePermission(userId, operation.OperationID))
                {
                    list.Add((Operation)operation.Clone());
                }
            }
            list.Sort((x, y) => Comparer<int>.Default.Compare(x.Index, y.Index));
            return list;
        }

        #endregion
    }
}